iT邦幫忙

2024 iThome 鐵人賽

DAY 27
0

前幾天都在趕進度,今天稍稍放慢腳步補充之前在 Day11 提到的依賴注入(Dependency Injection)。這個設計模式在大型專案中尤為重要,我們可以利用它來降低耦合度、提高可測試性,並且更靈活地管理物件之間的依賴關係。

什麼是依賴注入?

依賴注入的核心概念很簡單,就是將一個物件的依賴交由外部來管理,而不是在物件內部自行建立或初始化。這樣做的好處是,我們可以靈活替換依賴物件,進而提高模組化和測試性。

舉個簡單例子,我們先來看一個沒有使用依賴注入的程式碼範例:

class UserManager {
    private let networkService = NetworkService()

    func fetchUserData() {
        networkService.requestData()
    }
}

這裡 UserManager 直接依賴於 NetworkService,這會導致無法輕鬆替換 NetworkService(例如在測試環境中),並且兩個類之間的耦合度過高。

改進:使用依賴注入

我們可以透過依賴注入來改進這個結構,讓 UserManager 接收 NetworkService 作為外部傳入的依賴:

class UserManager {
    private let networkService: NetworkService

    init(networkService: NetworkService) {
        self.networkService = networkService
    }

    func fetchUserData() {
        networkService.requestData()
    }
}

這樣 UserManager 就不再自行管理 NetworkService 的實例,而是依賴於外部注入的 NetworkService,使得我們可以輕鬆地替換為不同的實作,例如在測試時使用 mock 物件。

在 SwiftUI 中實作依賴注入

在 SwiftUI 中,我們可以透過 @EnvironmentObject@StateObject 等方式來進行依賴注入。例如,我們可以將 DataManager 作為依賴注入到多個 View 中。

定義 DataManager

首先,我們來定義一個簡單的 DataManager 類,用來管理資料操作。

class DataManager: ObservableObject {
    @Published var items: [Item] = []

    func fetchItems() {
        // 模擬資料抓取
        self.items = [
            Item(name: "蘋果", quantity: 2, price: 30),
            Item(name: "橘子", quantity: 3, price: 50)
        ]
    }

    func addItem(_ item: Item) {
        items.append(item)
    }
}

這個 DataManager 是一個 ObservableObject,我們可以將它注入到 View 中,讓不同的 View 共享資料。

將 DataManager 注入到 Parent View

在 SwiftUI 中,我們可以在 App 或 Scene 中使用 @StateObject 來建立 DataManager,然後將它注入到子 View 中。

@main
struct MyApp: App {
    @StateObject private var dataManager = DataManager()

    var body: some Scene {
        WindowGroup {
            ContentView()
                .environmentObject(dataManager)  // 注入 DataManager 到環境中
        }
    }
}

這樣,DataManager 就會被注入到 ContentView 以及所有子 View 的環境中,我們可以在任意一個 View 中取得 DataManager。

在子 View 中使用注入的依賴

接著,我們在 ContentView 中注入 DataManager,並展示資料。

struct ContentView: View {
    @EnvironmentObject var dataManager: DataManager  // 注入 DataManager

    var body: some View {
        NavigationView {
            List {
                ForEach(dataManager.items) { item in
                    Text("\(item.name): \(item.quantity) 個 - \(item.price) 元")
                }
            }
            .navigationTitle("家用品清單")
            .onAppear {
                dataManager.fetchItems()  // 當 View 顯示時抓取資料
            }
        }
    }
}

在這裡,@EnvironmentObject 自動從上層環境中取得 DataManager,這樣 ContentView 就不需要自行管理資料的來源,依賴關係也變得更加靈活。

依賴注入的優點

  • 降低耦合度:透過依賴注入,我們可以避免類與類之間直接的依賴,這樣修改一個類時不會影響到另一個類。
  • 提高可測試性:我們可以在測試環境中輕鬆地替換掉真實物件,例如將網路服務替換成 Mock 物件,這樣可以進行單元測試。
  • 靈活性:透過依賴注入,我們可以輕鬆地切換不同的服務實作,例如使用不同的資料來源(本地端、遠端、測試資料等)。

依賴注入的缺點

  • 增加系統複雜性:實現依賴注入時,系統的結構變得更加複雜,在大型專案中,開發速度變慢,維護成本也會隨之增加。
  • 難以追蹤依賴:過度使用依賴注入可能導致系統中的物件之間的關聯變得模糊,難以追蹤,降低了系統的可讀性。
  • 程式碼分散:依賴注入使得依賴關係的管理分散在不同的地方,可能讓理解和維護程式碼變得更加困難。
  • 學習成本較高:對於新手來說,理解和掌握依賴注入的概念和應用可能需要更多時間和學習成本。

總結

今天我們認識依賴注入的概念,並在 SwiftUI 中實作了這個設計模式。透過依賴注入,我們可以減少物件之間的耦合,提升系統的可維護性與測試性。特別是在大型專案中,依賴注入可以有效地幫助我們管理物件間的依賴,讓系統更加靈活。明天我們將繼續優化我們的專案,敬請期待!

參考資料:


上一篇
Day 26: SwiftUI 編輯與儲存掃描到的消費清單
下一篇
Day 28: SwiftUI 顯示月份與自定義月份選擇器實作
系列文
用 SwiftUI 掌控家庭日用品庫存30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言